// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Jeff Stewart
//
// Base class for all probes.  A probe exercises some function and returns
// both status in an integral form, as a localized string for local display,
// and as an English string for debug reporting.

#include <stdarg.h>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QTimer>
#include "probes/probe.h"

namespace popdiag {

ToStringLog::ToStringLog()
  : QTextStream(&text_, QIODevice::WriteOnly),
    is_open_(false) {
}

bool Probe::debug_ = false;

Probe::Probe(const QString &name)
  : name_(name),
    execute_status_(NOT_EXECUTED),
    result_status_(PROBE_FAILED) {
}

Probe::~Probe() {
}

void Probe::Start() {
  CHECK(execute_status_ == NOT_EXECUTED);
  execute_status_ = RUNNING;
  CHECK(connect(this, SIGNAL(ProbeComplete(ResultStatus)),
                this, SLOT(MarkExecuted(ResultStatus))));
  this->StartProbe();
}

void Probe::MarkExecuted(ResultStatus result) {
  if (execute_status_ == FINISHED) {
    // NEVER double-issue complete.
    return;
  }
  result_status_ = result;
  execute_status_ = FINISHED;
  emit Complete();
}

void Probe::StartLogDetails() {
  CHECK(!log_.IsOpen());
  log_.Open();
  log_ << "<table border=1 class=\"probe\" id=\"" << name() << "\">\n";
  log_ << "<tr><th>Time</th><th>Step</th><th>OK?</th><th>Details</th></tr>\n";
}

void Probe::LogDetails(const QString &step, bool good,
                           const QString &details) {
  if (!log_.IsOpen()) {
    // free the derived probes to LogDetails after closing the log.
    // it's easier than tracking down every path that logs after the
    // fault is identified.
    return;
  }
  const char *color = good ? "#00FF00" : "#ff0000";
  QString now = QTime::currentTime().toString("hh:mm:ss.zzzz");
  log_ << "<tr BGCOLOR=\"" << color << "\"><td>"
    << now << "</td><td>"
    << step << "</td><td>" << (good ? "OK" : "FAIL") << "</td><td>"
    // Do not HTML escape the details as any formatting there we
    // put in ourselves.
    << (details.isEmpty() ? "&nbsp;" : details) << "</td></tr>\n";
}

void Probe::EndLogDetails() {
  CHECK(log_.IsOpen());
  log_ << "</table>\n";
  log_.Close();
}

void Probe::Debug(const char *fmt, ...) {
  if (debug_) {
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
  }
}

void Probe::PrintDebuggingInformation(bool doit) {
  debug_ = doit;
}

ProbeSet::ProbeSet(QObject *parent)
  : QObject(parent), mutex_(), probes_(), next_probe_(probes_.end()) {
}

ProbeSet::~ProbeSet() {
  for (Storage::iterator i = probes_.begin(); i != probes_.end(); i++) {
    delete *i;
  }
  probes_.clear();
}

void ProbeSet::AddProbe(Probe *p) {
  QMutexLocker lock(&mutex_);
  probes_.append(p);
}

void ProbeSet::GetProbes(Storage *out) {
  QMutexLocker lock(&mutex_);
  out->clear();
  for (Storage::iterator i = probes_.begin(); i != probes_.end(); i++) {
    out->append(*i);
  }
  return;
}

void ProbeSet::Rewind() {
  QMutexLocker lock(&mutex_);
  next_probe_ = probes_.begin();
}

Probe *ProbeSet::NextProbe() {
  QMutexLocker lock(&mutex_);
  if (next_probe_ == probes_.end()) {
    return NULL;
  }
  Probe *rval = *next_probe_;
  ++next_probe_;
  return rval;
}

void RunWhenEventQueueEmpties(QObject* object, const char* slot) {
  // timeout of 0 means run when the queue empties
  QTimer::singleShot(0, object, slot);
}

}  // namespace popdiag
